/*  _______         ____    __         ___    ___
 * \    _  \       \    /  \  /       \   \  /   /       '   '  '
 *  |  | \  \       |  |    ||         |   \/   |         .      .
 *  |  |  |  |      |  |    ||         ||\  /|  |
 *  |  |  |  |      |  |    ||         || \/ |  |         '  '  '
 *  |  |  |  |      |  |    ||         ||    |  |         .      .
 *  |  |_/  /        \  \__//          ||    |  |
 * /_______/ynamic    \____/niversal  /__\  /____\usic   /|  .  . ibliotheque
 *                                                      /  \
 *                                                     / .  \
 * rendsig.c - Wrappers to render samples from        / / \  \
 *             the signals in a DUH.                 | <  /   \_
 *                                                   |  \/ /\   /
 * By entheh.                                         \_  /  > /
 *                                                      | \ / /
 *                                                      |  ' /
 *                                                       \__/
 */

#include <stdlib.h>

#include "dumb.h"
#include "internal/dumb.h"

struct DUH_SIGRENDERER {
    DUH_SIGTYPE_DESC *desc;

    sigrenderer_t *sigrenderer;

    int n_channels;

    long pos;
    int subpos;

    DUH_SIGRENDERER_SAMPLE_ANALYSER_CALLBACK callback;
    void *callback_data;
};

DUH_SIGRENDERER *duh_start_sigrenderer(DUH *duh, int sig, int n_channels,
                                       long pos) {
    DUH_SIGRENDERER *sigrenderer;

    DUH_SIGNAL *signal;
    DUH_START_SIGRENDERER proc;

    if (!duh)
        return NULL;

    if ((unsigned int)sig >= (unsigned int)duh->n_signals)
        return NULL;

    signal = duh->signal[sig];
    if (!signal)
        return NULL;

    sigrenderer = malloc(sizeof(*sigrenderer));
    if (!sigrenderer)
        return NULL;

    sigrenderer->desc = signal->desc;

    proc = sigrenderer->desc->start_sigrenderer;

    if (proc) {
        duh->signal[sig] = NULL;
        sigrenderer->sigrenderer =
            (*proc)(duh, signal->sigdata, n_channels, pos);
        duh->signal[sig] = signal;

        if (!sigrenderer->sigrenderer) {
            free(sigrenderer);
            return NULL;
        }
    } else
        sigrenderer->sigrenderer = NULL;

    sigrenderer->n_channels = n_channels;

    sigrenderer->pos = pos;
    sigrenderer->subpos = 0;

    sigrenderer->callback = NULL;

    return sigrenderer;
}

void duh_sigrenderer_set_sample_analyser_callback(
    DUH_SIGRENDERER *sigrenderer,
    DUH_SIGRENDERER_SAMPLE_ANALYSER_CALLBACK callback, void *data) {
    if (sigrenderer) {
        sigrenderer->callback = callback;
        sigrenderer->callback_data = data;
    }
}

int duh_sigrenderer_get_n_channels(DUH_SIGRENDERER *sigrenderer) {
    return sigrenderer ? sigrenderer->n_channels : 0;
}

long duh_sigrenderer_get_position(DUH_SIGRENDERER *sigrenderer) {
    DUH_SIGRENDERER_GET_POSITION proc;

    if (!sigrenderer)
        return -1;

    proc = sigrenderer->desc->sigrenderer_get_position;
    if (proc)
        return (*proc)(sigrenderer->sigrenderer);
    else
        return sigrenderer->pos;
}

void duh_sigrenderer_set_sigparam(DUH_SIGRENDERER *sigrenderer,
                                  unsigned char id, long value) {
    DUH_SIGRENDERER_SET_SIGPARAM proc;

    if (!sigrenderer)
        return;

    proc = sigrenderer->desc->sigrenderer_set_sigparam;
    if (proc)
        (*proc)(sigrenderer->sigrenderer, id, value);
    else
        TRACE("Parameter #%d = %ld for signal %c%c%c%c, which does not take "
              "parameters.\n",
              (int)id, value, (int)(sigrenderer->desc->type >> 24),
              (int)(sigrenderer->desc->type >> 16),
              (int)(sigrenderer->desc->type >> 8),
              (int)(sigrenderer->desc->type));
}

long duh_sigrenderer_generate_samples(DUH_SIGRENDERER *sigrenderer,
                                      float volume, float delta, long size,
                                      sample_t **samples) {
    long rendered;
    LONG_LONG t;

    if (!sigrenderer)
        return 0;

    rendered = (*sigrenderer->desc->sigrenderer_generate_samples)(
        sigrenderer->sigrenderer, volume, delta, size, samples);

    if (rendered) {
        if (sigrenderer->callback)
            (*sigrenderer->callback)(sigrenderer->callback_data,
                                     (const sample_t *const *)samples,
                                     sigrenderer->n_channels, rendered);

        t = sigrenderer->subpos + (LONG_LONG)(delta * 65536.0 + 0.5) * rendered;

        sigrenderer->pos += (long)(t >> 16);
        sigrenderer->subpos = (int)t & 65535;
    }

    return rendered;
}

void duh_sigrenderer_get_current_sample(DUH_SIGRENDERER *sigrenderer,
                                        float volume, sample_t *samples) {
    if (sigrenderer)
        (*sigrenderer->desc->sigrenderer_get_current_sample)(
            sigrenderer->sigrenderer, volume, samples);
}

void duh_end_sigrenderer(DUH_SIGRENDERER *sigrenderer) {
    if (sigrenderer) {
        if (sigrenderer->desc->end_sigrenderer)
            if (sigrenderer->sigrenderer)
                (*sigrenderer->desc->end_sigrenderer)(sigrenderer->sigrenderer);

        free(sigrenderer);
    }
}

DUH_SIGRENDERER *duh_encapsulate_raw_sigrenderer(sigrenderer_t *vsigrenderer,
                                                 DUH_SIGTYPE_DESC *desc,
                                                 int n_channels, long pos) {
    DUH_SIGRENDERER *sigrenderer;

    if (desc->start_sigrenderer && !vsigrenderer)
        return NULL;

    sigrenderer = malloc(sizeof(*sigrenderer));
    if (!sigrenderer) {
        if (desc->end_sigrenderer)
            if (vsigrenderer)
                (*desc->end_sigrenderer)(vsigrenderer);
        return NULL;
    }

    sigrenderer->desc = desc;
    sigrenderer->sigrenderer = vsigrenderer;

    sigrenderer->n_channels = n_channels;

    sigrenderer->pos = pos;
    sigrenderer->subpos = 0;

    sigrenderer->callback = NULL;

    return sigrenderer;
}

sigrenderer_t *duh_get_raw_sigrenderer(DUH_SIGRENDERER *sigrenderer,
                                       long type) {
    if (sigrenderer && sigrenderer->desc->type == type)
        return sigrenderer->sigrenderer;

    return NULL;
}

#if 0
// This function is disabled because we don't know whether we want to destroy
// the sigrenderer if the type doesn't match. We don't even know if we need
// the function at all. Who would want to keep an IT_SIGRENDERER (for
// instance) without keeping the DUH_SIGRENDERER?
sigrenderer_t *duh_decompose_to_raw_sigrenderer(DUH_SIGRENDERER *sigrenderer, long type)
{
	if (sigrenderer && sigrenderer->desc->type == type) {



	if (sigrenderer) {
		if (sigrenderer->desc->end_sigrenderer)
			if (sigrenderer->sigrenderer)
				(*sigrenderer->desc->end_sigrenderer)(sigrenderer->sigrenderer);

		free(sigrenderer);
	}






		return sigrenderer->sigrenderer;
	}

	return NULL;
}
#endif
